spirv-std functions to query compute builtins#535
spirv-std functions to query compute builtins#535fluffysquirrels wants to merge 6 commits intoRust-GPU:move_modfrom
Conversation
|
Give me a little bit more time to work on my version. Don't get me wrong, I think your version is fine, I'm just wondering if we can extract more information from the intrinsics. Like |
|
I was also wondering about safe collection patterns without data races, the equivalent of That is still possible later even if |
|
Here are some thoughts on safe GPU collections. Is there already a discussion thread or issue for these, or should we make one? In my compute project I'm playing with implementing some standard algorithms, e.g. reduce, scan, filter, map. For coalesced access I'm using the normal pattern: each workgroup takes a slice of the total data, for each thread operates on 1 value at a time at index I was thinking a workgroup-level data-race-free API could expose this as a For simple compute grids with multiple workgroups, the collection For prefix scan I'm implementing approximately StreamScan (best described IMO in NVIDIA's "Single-pass Parallel Prefix Scan with Decoupled Look-back" paper, if you don't know it). I use a pattern where each workgroup increments a global atomic to take the next sub-slice of data to work on, and increments another when complete. Once all blocks are assigned, workgroups spin-wait on the completed blocks count to reach the expected final count; this acts as a global barrier before proceeding to the next phase of the algorithm. This seemed sufficiently useful and general to extract into a re-usable algorithm. It would be fantastic for the API to mutably borrow the output collections during such a phase, hopefully guaranteeing data-race-freedom. I was thinking a phase could be modelled like a |
|
There's a bug in the new linker dedupe pass I'm half way through fixing. |
|
Linker builtin de-dupe bug fixed! |
|
Fixed the minor test errors on 4282ef0. |
|
I dumped my thoughts here: Rust-GPU/rust-gpu.github.io#96 TLDR:
|
|
Fixed a few lint errors in d6fc65b. 🤞 |
|
I'm cherry-picking out the builtin dedup linker pass in a separate PR, squashed it down a bit and cleaned up the pass. Primarily, an OpVariable should only have one Builtin decoration. #539 It's just simpler to get a fix like that through reviews, and separate out the API design questions of the new builtin getters. |
94216a1 to
2a9576c
Compare
|
Rebased it on #539, squashed it and fixed missing GLSL builtin names. I'll take another look at this tomorrow and may just copy-paste together all the remaining built-ins. |
|
Cool.
Great to have those. I didn't look for them in the GLSL extensions, oops! |
2a9576c to
b62cd33
Compare
b62cd33 to
08ccd9d
Compare
|
Or rather, github is displaying my pfp, even though the commit says it's your commits? @fluffysquirrels FYI you're using a different commit email than the one listed on your profile, so github is marking these commits as made by an unknown contributor. Either change your commit email or add your commit |
|
I'm gonna repurpose this PR to add just the compute and subgroup builtin getters, and worry about the graphics stuff in another PR. Which also means I'm merging #539 into this one, to have one complete working implementation, instead of splitting it up into two PRs. |
08ccd9d to
286d803
Compare
0f1b95b to
708a6d6
Compare
708a6d6 to
adcba8d
Compare
|
@fluffysquirrels I may have found an entirely new way to implement the linker fixup... The existing type dedup pass added the name (as in I think this should also be fine with other usages of OpVariable. The key includes all other decorations on the variable, so buffers should work just fine since they have |
adcba8d to
f382cfb
Compare
| /// Query SPIR-V (read-only global) built-in values | ||
| /// | ||
| /// See [module level documentation] on how to use these. | ||
| #[macro_export] |
There was a problem hiding this comment.
I deliberately kept load_builtin! private as an implementation detail. As is, it's very unsafe. Also easy to make something that won't compile but will have an inscrutable error message, or won't validate.
I think this should remain an internal implementation detail.
There was a problem hiding this comment.
Separating out all the builtins into modules in the root makes it a little harder to find them all. I think (especially for compute), someone using the subgroup builtins are also likely to use the compute builtins, so keeping these modules nested under crate::builtin makes sense to me.
In std (and other crates) I've seen people keep all the low level primitives / intrinsics in std::arch or similar. I think that pattern makes sense.
Any higher-level abstraction on top such as safe collections (alloc::Vec in std or whatever parallel collection for spirv here) could then have its own module in the root.
There was a problem hiding this comment.
This won't work for the graphics builtins. Here's a message I sent internally last week about this:
- Easy case: Some built-ins can be "inherited", eg.
local_invocation_idfrom compute shaders are used in mesh shaders and ray gen shaders, cause they fundamentally function like augmented compute shaders. Will likely just define it once for compute shaders and you can just reuse them. - medium case:
gl_PrimitiveID- vertex, tessellation & fragment shaders: an input
- geometry shaders:
gl_PrimitiveIDIn: an inputgl_PrimitiveID: an output
- mesh shaders:
gl_MeshPerPrimitiveEXT[].gl_PrimitiveIDunsized per-primitive output array.
- Hard case:
gl_Position- vertex shader: an output variable that is written to via
gl_Position - tessellation control | evaluation shaders:
gl_in[gl_MaxPatchVertices].gl_Position: a sized arrayed input variable you can read to get the Nth vertex positiongl_Position: an output variable you can write
- geometry shader:
- an unsized arrayed input variable you can read to get the Nth vertex position via
gl_in.gl_Position gl_Position: an output variable you can write
- an unsized arrayed input variable you can read to get the Nth vertex position via
- vertex shader: an output variable that is written to via
Requires #540
This PR adds new API to query compute and subgroup builtins via getter functions:
spirv_std::computewith builtins:local_invocation_index() -> u32local_invocation_id() -> UVec3global_invocation_id() -> UVec3num_workgroups() -> UVec3workgroup_id() -> UVec3gl_WorkgroupSizeequivalent, see belowspirv_std::subgroup:num_subgroups() -> u32subgroup_id() -> u32subgroup_size() -> u32subgroup_invocation_id() -> u32subgroup_*_mask() -> SubgroupMaskabout
gl_WorkgroupSizeIn spirv, there's a
WorkgroupSizebuilt-in which you have to apply not to anOpVariablelike every other built-in but to anOpConstant, and applying built-ins to constants has been deprecated. Instead, they recommend that shader compilers look at theExecutionMode LocalSizeof their compute shader and just return that whengl_WorkGroupSizeis read. glslc literally compiles agl_WorkGroupSizeread into%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3without even decorating it withWorkgroupSizesince that's been deprecated. But that's not trivial in rust-gpu, since we may have a single module with two compute shaders, and each compute shader may have a different workgroup size. So anfn workgroup_size() -> UVec3intrinsic would need to be somehow specialized per shader, even if it is called through some non-generic function both shaders use. Possible solutions:workgroup_size()to specialize it, but since there's nothing passed to the function by reference, will likely be annoying to implement.OpVaraiablethat is filled with the value ofWorkGroupSizeby the entry point, and that intrinsic just reads it. Would require support forstatics, which I'd honestly like to have for other custom use-cases as well.I don't feel like we need to support this right away and can delay it's implementation.
old
This is a continuation of Firestar99's work in
#459 . I created a new PR only to have somewhere to show my changes.
A small problem: if an entry-point accepts a builtin as a parameter and code is generated for the get function for that parameter, then 2 globals are emitted decorated with the same builtin, and validation fails.
See the failing test
compute_duplicates.SPIR-V output:
compiletest error message:
For ease of use and porting to the new bulitin getters, I'd like to have this compile successfully by de-duplicating the global variable for the builtin. I think I see similar code in
/crates/rustc_codegen_spirv/src/linker/duplicates.rs, is that the right way to handle it? I will try to write this.I also considered building a map between builtin names and result ID's in the assembler and the entry-point codegen. I think that might run faster, but would have higher code complexity and need to touch more places.